#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
缓存管理模块

为剑网3装备数据API提供内存缓存功能，提升响应速度
"""

import asyncio
import time
import hashlib
import json
from typing import Dict, Any, Optional, Callable
from functools import wraps
from dataclasses import dataclass, field
from config import server_config


@dataclass
class CacheItem:
    """缓存项数据结构"""
    
    data: Any                           # 缓存的数据
    timestamp: float                    # 创建时间戳
    ttl: int                           # 生存时间(秒)
    hit_count: int = 0                 # 命中次数
    last_access: float = field(default_factory=time.time)  # 最后访问时间
    
    def is_expired(self) -> bool:
        """检查缓存项是否过期"""
        return time.time() - self.timestamp > self.ttl
    
    def update_access(self):
        """更新访问信息"""
        self.hit_count += 1
        self.last_access = time.time()


class MemoryCache:
    """内存缓存管理器"""
    
    def __init__(self, max_size: int = 1000, default_ttl: int = 300):
        """
        初始化缓存管理器
        
        Args:
            max_size: 最大缓存项数量
            default_ttl: 默认TTL时间(秒)
        """
        self._cache: Dict[str, CacheItem] = {}
        self._max_size = max_size
        self._default_ttl = default_ttl
        self._lock = asyncio.Lock()
        
        # 统计信息
        self._stats = {
            "hits": 0,
            "misses": 0, 
            "evictions": 0,
            "total_requests": 0
        }
    
    def _generate_key(self, prefix: str, **kwargs) -> str:
        """
        生成缓存键
        
        Args:
            prefix: 键前缀
            **kwargs: 参数字典
            
        Returns:
            缓存键字符串
        """
        # 创建一个稳定的键，忽略参数顺序
        params_str = json.dumps(kwargs, sort_keys=True, ensure_ascii=False)
        key_content = f"{prefix}:{params_str}"
        return hashlib.md5(key_content.encode()).hexdigest()
    
    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存数据
        
        Args:
            key: 缓存键
            
        Returns:
            缓存的数据，如果不存在或过期返回None
        """
        async with self._lock:
            self._stats["total_requests"] += 1
            
            if key not in self._cache:
                self._stats["misses"] += 1
                return None
            
            item = self._cache[key]
            
            # 检查是否过期
            if item.is_expired():
                del self._cache[key]
                self._stats["misses"] += 1
                return None
            
            # 更新访问信息
            item.update_access()
            self._stats["hits"] += 1
            
            return item.data
    
    async def set(self, key: str, data: Any, ttl: Optional[int] = None) -> None:
        """
        设置缓存数据
        
        Args:
            key: 缓存键
            data: 要缓存的数据
            ttl: 生存时间，None使用默认值
        """
        async with self._lock:
            # 使用默认TTL如果未指定
            if ttl is None:
                ttl = self._default_ttl
            
            # 如果缓存已满，执行LRU清理
            if len(self._cache) >= self._max_size and key not in self._cache:
                await self._evict_lru()
            
            # 创建缓存项
            self._cache[key] = CacheItem(
                data=data,
                timestamp=time.time(),
                ttl=ttl
            )
    
    async def _evict_lru(self) -> None:
        """使用LRU策略清理缓存"""
        if not self._cache:
            return
        
        # 找到最久未访问的项目
        lru_key = min(
            self._cache.keys(),
            key=lambda k: self._cache[k].last_access
        )
        
        del self._cache[lru_key]
        self._stats["evictions"] += 1
    
    async def clear(self) -> None:
        """清空所有缓存"""
        async with self._lock:
            self._cache.clear()
            print("🗑️ 缓存已清空")
    
    async def cleanup_expired(self) -> int:
        """清理过期的缓存项"""
        async with self._lock:
            expired_keys = [
                key for key, item in self._cache.items()
                if item.is_expired()
            ]
            
            for key in expired_keys:
                del self._cache[key]
            
            return len(expired_keys)
    
    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计信息"""
        hit_rate = 0.0
        if self._stats["total_requests"] > 0:
            hit_rate = self._stats["hits"] / self._stats["total_requests"]
        
        return {
            **self._stats,
            "hit_rate": hit_rate,
            "cache_size": len(self._cache),
            "max_size": self._max_size
        }
    
    def reset_stats(self) -> None:
        """重置统计信息"""
        self._stats = {
            "hits": 0,
            "misses": 0,
            "evictions": 0, 
            "total_requests": 0
        }


# 全局缓存实例
cache_manager = MemoryCache(
    max_size=server_config.cache_size,
    default_ttl=server_config.cache_ttl
) if server_config.enable_cache else None


def cached(prefix: str, ttl: Optional[int] = None):
    """
    缓存装饰器
    
    Args:
        prefix: 缓存键前缀
        ttl: 缓存时间，None使用默认值
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            # 如果缓存未启用，直接调用函数
            if not server_config.enable_cache or cache_manager is None:
                return await func(*args, **kwargs)
            
            # 生成缓存键
            cache_key = cache_manager._generate_key(prefix, **kwargs)
            
            # 尝试从缓存获取
            cached_result = await cache_manager.get(cache_key)
            if cached_result is not None:
                return cached_result
            
            # 缓存未命中，调用原函数
            result = await func(*args, **kwargs)
            
            # 存储到缓存
            await cache_manager.set(cache_key, result, ttl)
            
            return result
        
        return wrapper
    return decorator


async def start_cache_cleanup_task():
    """启动定期清理过期缓存的后台任务"""
    if not server_config.enable_cache or cache_manager is None:
        return
    
    async def cleanup_task():
        while True:
            try:
                # 每5分钟清理一次过期缓存
                await asyncio.sleep(300)
                expired_count = await cache_manager.cleanup_expired()
                if expired_count > 0:
                    print(f"🧹 清理了 {expired_count} 个过期缓存项")
            except Exception as e:
                print(f"❌ 缓存清理任务错误: {e}")
    
    # 创建后台任务
    asyncio.create_task(cleanup_task())
    print("🚀 缓存清理任务已启动") 